/*
 * Botonera.java
 * Copyright (C) 2007 Ezequiel Turovetzky
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 3
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, see <http://www.gnu.org/licenses/>.
 */
package ar.com.botonera.gui;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.GridLayout;
import java.awt.LayoutManager;
import java.awt.dnd.DropTarget;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import javax.swing.JLabel;
import javax.swing.JPanel;

import ar.com.botonera.Application;
import ar.com.botonera.Botonera;
import ar.com.botonera.config.BotoneraManager;
import ar.com.botonera.config.Configuration;
import ar.com.botonera.config.Configurator;
import ar.com.botonera.config.Entry;
import ar.com.botonera.gui.common.DropButtonAdapterFactory;
import ar.com.botonera.gui.common.Messenger;
import ar.com.botonera.gui.common.listeners.HideContextualMenuAdapter;
import ar.com.botonera.gui.menu.contextual.ContextualMenu;
import ar.com.botonera.utils.ExceptionHandlerFactory;
import ar.com.botonera.utils.FileUtils;

/**
 * Implementaci&oacute;n con <code>Swing</code> de la botonera.
 * <br>
 * Es un panel al que se le pueden agregar botones, del tipo
 * <code>SoundButton</code>.
 * <br>Estos botones deben ser agregados a trav&eacute;s de los m&eacute;todos
 * que esta clase provee: <code>addButton(SoundButton)</code> y/o
 * <code>addButton(java.io.File)</code>. 
 * <br>Para agregar estos botones
 * <b>no deben usarse</b> los m&eacute;todos <code>add(Component)</code>
 * heredados de las superclases de esta clase, ya que de hacerlo,
 * se perder&aacute; control sobre los botones y se obtendr&aacute;n resultados
 * inesperados. 
 * @author <a href="mailto:tezequiel@gmail.com">Ezequiel Turovetzky</a>
 * @version 1.0
 */
public class JBotonera extends JPanel implements Botonera {

    //~ Instance fields --------------------------------------------------------

    /** Menu contextual mostrado al hacer click derecho sobre los botones */
    private ContextualMenu contextualMenu;
    /** Cantidad de botones en la botonera */
    private int buttonCount;
    /** Label utilizado cuando no hay botones en la botonera */
    private JLabel noButtonsFoundLabel;
    /** Layout utilizado cuando hay botones en la botonera */
    private LayoutManager noEmptyLayout;
    /** Layout utilizado cuando no hay botones en la botonera */
    private LayoutManager emptyLayout;

    //~ Constructors -----------------------------------------------------------

    /**
     * Crea una nueva instancia de esta clase.
     */
    public JBotonera() {
        setBackground(Color.WHITE);

    	contextualMenu = new ContextualMenu();
    	noButtonsFoundLabel = new NoButtonsFoundLabel();
    	emptyLayout = getLayout();

    	Configuration cfg = Configurator.getInstance().getConfiguration();
        
    	noEmptyLayout = new GridLayout(0, cfg.getButtonsPerRow());

        if(cfg.entries().isEmpty()) {
        	
        	add(noButtonsFoundLabel, BorderLayout.CENTER);
        
        } else {
        	
            for(Entry entry : cfg.entries()) {
                SoundButton button = new SoundButton(entry.getLabel(), new File(entry.getFile()));
                Application.getLogger().debug("Agregando boton: " + button);
                add(button);
                buttonCount++;
            }
            
        	setLayout(noEmptyLayout);
        }

        addMouseListener(new HideContextualMenuAdapter());
        setDropTarget(new DropTarget(this, DropButtonAdapterFactory.getAdapter()));
    }

    //~ Methods ----------------------------------------------------------------

    /**
     * Agrega un bot&oacute;n a esta botonera.
     * @param button El bot&oacute;n a agregar.
     */
    public void addButton(SoundButton button) {
    	// Si no hay botones, saco el cartel de "No hay botones" y cambio el layout
    	if(!hasButtons()) {
    		remove(noButtonsFoundLabel);
    		setLayout(noEmptyLayout);
    	}
    	if(!containsButtonLabel(button.getText())) {
    		Application.getLogger().debug("Agregando boton: " + button);
    		add(button);
    		updateUI();
    		Configuration cfg = Configurator.getInstance().getConfiguration();
    		cfg.addEntry(button.getText(), button.getMp3().getAbsolutePath());
    		buttonCount++;
    	} else {
    		BotoneraManager bm = BotoneraManager.getInstance();
    		Messenger.showErrorMessage(bm.get("ar.com.botonera.label.exists.title"), 
    				bm.get("ar.com.botonera.label.exists.message"));
    	}
    }
    
    /**
     * Agrega un bot&oacute;n a la botonera a partir de un archivo.
     * <br>En caso de especificarse un directorio, se recorrer&aacute;
     * recursivamente el directorio y sus subdirectorios, agregando a
     * esta botonera <b>todos</b> los archivos <code>mp3</code> que se encuentren.
     * @param file Archivo o directorio a agregar a esta botonera.
     */
    public void addButton(File file) {
    	try {
    		
    		if(file.isDirectory()) {
    			Application.getLogger().debug("Agregando botones de directorio: " + file.getAbsolutePath());
    			for(File f : file.listFiles()) {
    				addButton(f); // recursividad
    			}
    		} else if(FileUtils.isMp3(file)) {
    	          SoundButton button = new SoundButton(file.getName(),file.getCanonicalFile());
    	          addButton(button);
    		} else {
    			Messenger.showErrorMessage("Error", "El archivo no es un archivo mp3: " + file.getName());
    		}

    	} catch(IOException e) {
    		ExceptionHandlerFactory.getExceptionHandler().handle(e);
    	}
    }

    /**
     * Quita un bot&oacute;n de esta botonera.
     * @param button El bot&oacute;n a quitar.
     */
    public void removeButton(SoundButton button) {
    	Application.getLogger().debug("Eliminando boton: " + button);
        remove(button);
        updateUI();
        Configurator.getInstance().getConfiguration().removeEntry(button.getText());
        buttonCount--;
        // Si no quedan botones, cambio el layout y pongo el cartel de "No hay botones"
        if(!hasButtons()) {
        	setLayout(emptyLayout);
        	add(noButtonsFoundLabel, BorderLayout.CENTER);
        }
    }
    
    /**
     * Devuelve una lista con todos los botones
     * de esta botonera.
     * @return Lista con todos los botones de esta botonera.
     */
    public List<SoundButton> getButtons() {
    	List<SoundButton> buttons = new ArrayList<SoundButton>();
    	for(Component c : getComponents()) {
    		if(c instanceof SoundButton) {
    			buttons.add((SoundButton)c);
    		}
    	}
    	return buttons;
    }

    /**
     * Informa si existe un boto&oacute;n en la botonera con 
     * el label especificado.
     * @param label Label del bot&acute;n.
     * @return <code>true</code> si existe, <code>false</code> en caso opuesto.
     */
    public boolean containsButtonLabel(String label) {
    	for(SoundButton button : getButtons()) {
    		if(button.getText().equals(label)) {
    			return true;
    		}
    	}
    	return false;
    }
    
    /**
     * Establece la cantidad de botones por fila que se visualizan
     * en la botonera.
     * <br>Este m&eacute;todo es utilizado &uacute;nicamente para cambiar en tiempo
     * de ejecuci&oacute;n la forma en que se visualizan los botones.
     * @param buttonsPerRow de botones por fila que se visualizan en la botonea.
     */
    public void setButtonsPerRow(int buttonsPerRow) {
    	Configurator.getInstance().getConfiguration().setButtonsPerRow(buttonsPerRow);
    	((GridLayout)noEmptyLayout).setColumns(buttonsPerRow);
    	updateUI();
    }
    
    /**
     * Devuelve la cantidad de botones por fila que se visualizan
     * en la botonera.
     * @return Cantidad de botones por fila que se visualizan en la botonera.
     */
    public int getButtonsPerRow() {
    	return Configurator.getInstance().getConfiguration().getButtonsPerRow();
    }
    
    /**
     * Informa si esta botonera tiene botones.
     * @return <code>true</code> si tiene botones, <code>false</code>
     * en caso opuesto.
     */
    public boolean hasButtons() {
    	return buttonCount > 0;
    }
    
    /**
     * Devuelve la cantidad de botones que esta botonera tiene.
     * @return Cantidad de botones en la botonera.
     */
    public int getButtonCount() {
    	return buttonCount;
    }

    /**
     * Devuelve el men&uacute; contextual mostrado al hacer click
     * derecho sobre alg&uacute;n bot&oacute;n de la botonera.
     * @return El men&uacute; contextual.
     */
    public ContextualMenu getContextualMenu() {
        return contextualMenu;
    }
    
    /**
     * Quita todos los botones de esta botonera.
     */
    public void clear() {
    	removeAll();
    	updateUI();
    }
}